﻿using DotNet.Utilities;
using DotNet.Utilities.ConsoleHelper;
using GB28181.Sys.XML;
using PropertyChanged;
using SimpleSIPServer.SIP;
using SimpleSIPServer.SIPSorcery;
using SIPSorcery.SIP;
using SIPSorcery.SIP.App;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Xml.Linq;
using WPFTemplate;
using WPFTemplate.UserControls;
using WPFTemplate.WpfHelpers;

namespace SimpleSIPServer.ViewModels
{
    [AddINotifyPropertyChangedInterface]
    public class MainWindowViewModel : BindableBase
    {
        #region Bindable

        private ConfigItems _configs;
        /// <summary>
        /// 配置项
        /// </summary>
        public ConfigItems Configs
        {
            get
            {
                if (_configs == null)
                {
                    LoadConfigCommand.Execute(false);
                }

                return _configs;
            }

            set => _configs = value;
        }

        /// <summary>
        /// 消息框是否自动滚动（不滚动的话不附加新消息）;
        /// </summary>
        public bool IsAutoScroll { get; set; } = true;

        private string _Info = "";
        /// <summary>
        /// 信息窗内容;
        /// </summary>
        public string Info
        {
            get => _Info;

            set
            {
                if (IsAutoScroll)
                {
                    SetProperty(ref _Info, value);
                }
                else
                {
                    _Info = value;
                }
            }
        }

        /// <summary>
        /// 底部状态栏信息
        /// </summary>
        public string Status { get; set; } = "";

        /// <summary>
        /// 弹窗VM;
        /// </summary>
        public ConfirmBoxViewModel DialogVm { get; set; } = new ConfirmBoxViewModel();

        /// <summary>
        /// 是否回应未注册的心跳;
        /// </summary>
        public bool IsResponseToNotReg { get; set; } = false;

        /// <summary>
        /// 是否在注册后自动发起直播;
        /// </summary>
        public bool IsAutoLive { get; set; } = true;

        #endregion

        #region 成员

        /// <summary>
        /// 加锁对象
        /// </summary>
        private static object _lockObj = new object();

        #endregion

        #region SIP 相关成员

        /// <summary>
        /// SIP 传输层;
        /// </summary>
        private SIPTransport _sipTransport;

        /// <summary>
        /// (记录注册到服务器的 SIP 客户端) [SIP Username, Binding], tracks SIP clients that have registered with the server.
        /// </summary>
        public ObservableCollection<SIPAccountBinding> SipRegistrations { get; set; } = new ObservableCollection<SIPAccountBinding>();

        /// <summary>
        /// 选中客户端
        /// </summary>
        public SIPAccountBinding SelectedAccountBinding { get; set; }

        /// <summary>
        /// 设备信息类别列表
        /// </summary>
        public List<CommandType> InfoTypes { get; set; } = new List<CommandType> { CommandType.Catalog, CommandType.DeviceInfo, CommandType.DeviceStatus, };

        /// <summary>
        /// 选中的设备类别
        /// </summary>
        public CommandType SelectedType { get; set; } = CommandType.Catalog;

        #endregion

        public MainWindowViewModel()
        {
            Console.SetOut(new ConsoleWriter(ShowInfo));

            SetCommandMethod();
        }

        #region 辅助方法

        private void ShowInfo(string info)
        {
            if (Info.Length > 100000)
            {
                Info = $"(已删除一半信息)\r\n{Info.Remove(0, 50000)}";
            }

            Info += $"[{DateTime.Now:HH:mm:ss.ffff}] {info}\r\n\r\n";
        }

        #endregion

        #region Command

        /// <summary>
        /// 弹窗演示命令
        /// </summary>
        public ICommand DialogCommand { get; set; }

        /// <summary>
        /// 保存配置命令
        /// </summary>
        public ICommand SaveConfigCommand { get; set; }

        /// <summary>
        /// 载入配置命令
        /// </summary>
        public ICommand LoadConfigCommand { get; set; }

        /// <summary>
        /// 启动服务命令
        /// </summary>
        public ICommand StartServerCommand { get; set; }

        /// <summary>
        /// 停止服务命令
        /// </summary>
        public ICommand StopServerCommand { get; set; }

        /// <summary>
        /// 开始直播命令
        /// </summary>
        public ICommand StartVideoCommand { get; set; }

        /// <summary>
        /// 停止直播命令
        /// </summary>
        public ICommand StopVideoCommand { get; set; }

        /// <summary>
        /// 查询设备信息命令
        /// </summary>
        public ICommand QueryInfoCommand { get; set; }

        #endregion

        #region 命令方法

        /// <summary>
        /// 命令方法赋值(在构造函数中调用)
        /// </summary>
        private void SetCommandMethod()
        {
            DialogCommand ??= new RelayCommand(o => true, async o =>
            {
                await ConfirmBoxHelper.ShowMessage(DialogVm, "操作前通知", 6);

                await ConfirmBoxHelper.ShowConfirm(DialogVm, "您确定要进行此操作吗？", async () =>
                {
                    #region 业务方法

                    ShowInfo("开始业务操作...");

                    await Task.Delay(1000 * 3);

                    ShowInfo("完成业务操作.");

                    #endregion

                }, ShowInfo);

                await ConfirmBoxHelper.ShowMessage(DialogVm, "操作后通知");
            });

            SaveConfigCommand ??= new RelayCommand(o => true, async o =>
            {
                if (ConfigManager.SaveConfig(Configs))
                {
                    await ConfirmBoxHelper.ShowMessage(DialogVm, "保存配置成功");
                }
                else
                {
                    await ConfirmBoxHelper.ShowMessage(DialogVm, "保存配置失败");
                }
            });

            LoadConfigCommand ??= new RelayCommand(o => true, async o =>
            {
                if (ConfigManager.LoadConfig(ref _configs))
                {
                    if (o as bool? != false)
                    {
                        await ConfirmBoxHelper.ShowMessage(DialogVm, "载入配置成功");
                    }
                }
                else
                {
                    await ConfirmBoxHelper.ShowMessage(DialogVm, "载入配置失败");
                }
            });

            StartServerCommand ??= new RelayCommand(o => true, async o =>
            {
                StartServerHandler();
            });

            StopServerCommand ??= new RelayCommand(o => true, async o =>
            {
                await StopServerHandler();
            });

            StartVideoCommand ??= new RelayCommand(o => true, async o =>
            {
                await StartVideoHandler();
            });

            StopVideoCommand ??= new RelayCommand(o => true, async o =>
            {
                await StopVideoHandler();
            });

            QueryInfoCommand ??= new RelayCommand(o => true, async o =>
            {
                await QueryInfoHandler();
            });
        }

        /// <summary>
        /// 启动服务处理方法
        /// </summary>
        private void StartServerHandler()
        {
            try
            {
                Console.WriteLine("启动 SIP 服务...");

                //（配置SIP传输层） Configure the SIP transport layer.
                _sipTransport = new SIPTransport();

                //（使用默认选项配置 SIP 通道） Use default options to set up a SIP channel.
                var sipChannel = new SIPUDPChannel(new IPEndPoint(IPAddress.Any, Configs.ListenPort));
                _sipTransport.AddSIPChannel(sipChannel);

                var ipv6SipChannel = new SIPUDPChannel(new IPEndPoint(IPAddress.IPv6Any, Configs.ListenPort));
                _sipTransport.AddSIPChannel(ipv6SipChannel);

                //（接通传输层以便让 SIP 请求和响应有的放矢） Wire up the transport layer so SIP requests and responses have somewhere to go.
                _sipTransport.SIPTransportRequestReceived += SIPTransportRequestReceived;
                _sipTransport.SIPTransportResponseReceived += SIPTransportResponseReceived;

                //（如果想看所有 SIP 通信细节请接通以下事件） If you want to see ALL the nitty gritty SIP traffic wire up the events below.
                //_sipTransport.SIPBadRequestInTraceEvent += SIPBadRequestInTraceEvent;
                //_sipTransport.SIPBadResponseInTraceEvent += SIPBadResponseInTraceEvent;
                //_sipTransport.SIPRequestInTraceEvent += SIPRequestInTraceEvent;
                _sipTransport.SIPRequestOutTraceEvent += SIPRequestOutTraceEvent;
                //_sipTransport.SIPResponseInTraceEvent += SIPResponseInTraceEvent;
                _sipTransport.SIPResponseOutTraceEvent += SIPResponseOutTraceEvent;
            }
            catch (Exception ex)
            {
                Console.WriteLine($"异常：{ex}");
            }
        }

        /// <summary>
        /// 停止服务处理方法
        /// </summary>
        private async Task StopServerHandler()
        {
            try
            {
                Console.WriteLine("停止 SIP 服务...");

                foreach (SIPAccountBinding accountBinding in SipRegistrations)
                {
                    await SendBye(accountBinding);
                }

                _sipTransport.SIPTransportRequestReceived -= SIPTransportRequestReceived;
                _sipTransport.SIPTransportResponseReceived -= SIPTransportResponseReceived;
                _sipTransport.SIPBadRequestInTraceEvent -= SIPBadRequestInTraceEvent;
                _sipTransport.SIPBadResponseInTraceEvent -= SIPBadResponseInTraceEvent;
                _sipTransport.SIPRequestInTraceEvent -= SIPRequestInTraceEvent;
                _sipTransport.SIPRequestOutTraceEvent -= SIPRequestOutTraceEvent;
                _sipTransport.SIPResponseInTraceEvent -= SIPResponseInTraceEvent;
                _sipTransport.SIPResponseOutTraceEvent -= SIPResponseOutTraceEvent;

                SipRegistrations.Clear();

                _sipTransport.Shutdown();
                Console.WriteLine("已停止 SIP 服务.");
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
        }

        /// <summary>
        /// 开始直播视频指令的处理方法
        /// </summary>
        private async Task StartVideoHandler()
        {
            try
            {
                if (SelectedAccountBinding == null)
                {
                    await ConfirmBoxHelper.ShowMessage(DialogVm, "请在左侧选择一个目标项");
                    return;
                }

                SIPMethodsEnum method = SIPMethodsEnum.INVITE;
                string subject = SetSubject();
                string body = GetBodyLive(SelectedAccountBinding.SIPRequest.URI.User, Configs.MediaIP, Configs.MediaPort);

                await SendRequest(method, ConstString.Application_SDP, subject, body);
                await ConfirmBoxHelper.ShowMessage(DialogVm, $"已向[{SelectedAccountBinding.RemoteEndPoint.Address}]发送直播请求");
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
        }

        /// <summary>
        /// 停止直播处理方法
        /// </summary>
        /// <returns></returns>
        private async Task StopVideoHandler()
        {
            try
            {
                await ConfirmBoxHelper.ShowConfirm(DialogVm, $"确认要停止[{SelectedAccountBinding.RegisteredContact.User}]的直播吗？", async () =>
                {
                    await SendBye(SelectedAccountBinding);
                });
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
        }

        /// <summary>
        /// 查询设备信息处理方法
        /// </summary>
        /// <returns></returns>
        private async Task QueryInfoHandler()
        {
            try
            {
                SIPMethodsEnum method = SIPMethodsEnum.MESSAGE;
                string subject = SetSubject();
                string body = GetBodyQueryInfo(SelectedType);

                await SendRequest(method, ConstString.Application_MANSCDP, subject, body);
                await ConfirmBoxHelper.ShowMessage(DialogVm, $"已向[{SelectedAccountBinding.RemoteEndPoint.Address}]发送查询信息请求");
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
        }

        #endregion

        #region 处理 SIP 请求

        /// <summary>
        /// （处理 SIP 请求）Handler for processing incoming SIP requests.
        /// </summary>
        /// <param name="localSIPEndPoint">（[本地]接收者终节点）The end point the request was received on.</param>
        /// <param name="remoteEndPoint">（[远程]发送者终节点）The end point the request came from.</param>
        /// <param name="sipRequest">（接收的 SIP 请求）The SIP request received.</param>
        private async Task SIPTransportRequestReceived(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPRequest sipRequest)
        {
            try
            {
                Console.WriteLine($"从 [{remoteEndPoint}] 收到请求,方法【{sipRequest.Header.CSeqMethod}】,状态 [{sipRequest.StatusLine}].");

                if (sipRequest.Method == SIPMethodsEnum.BYE)
                {
                    await ByeHandler(localSIPEndPoint, remoteEndPoint, sipRequest);
                }
                else if (sipRequest.Method == SIPMethodsEnum.CANCEL)
                {
                    throw new NotImplementedException();
                }
                else if (sipRequest.Method == SIPMethodsEnum.INVITE)
                {
                    throw new NotImplementedException();
                }
                else if (sipRequest.Method == SIPMethodsEnum.OPTIONS)
                {
                    SIPResponse optionsResponse = SIPResponse.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null);
                    await _sipTransport.SendResponseAsync(optionsResponse);
                }
                else if (sipRequest.Method == SIPMethodsEnum.REGISTER)
                {
                    await RegisterHandler(localSIPEndPoint, remoteEndPoint, sipRequest);
                }
                else if (sipRequest.Method == SIPMethodsEnum.MESSAGE)
                {
                    await MessageHandler(localSIPEndPoint, remoteEndPoint, sipRequest);
                }
                else
                {
                    Console.WriteLine($"收到SIP方法 [{sipRequest.Method}] 的请求, 但没有设置处理方法, 略过.");
                }
            }
            catch (NotImplementedException)
            {
                Console.WriteLine($"请求方法 [{sipRequest.Method}] 的处理过程未实现（来自 [{remoteEndPoint}] 的 {sipRequest.URI.ToParameterlessString()}）.");

                SIPResponse notImplResponse = SIPResponse.GetResponse(sipRequest, SIPResponseStatusCodesEnum.NotImplemented, null);
                await _sipTransport.SendResponseAsync(notImplResponse);
            }
        }

        /// <summary>
        /// 告别消息（BYE）处理方法
        /// </summary>
        /// <param name="localSipEndPoint"></param>
        /// <param name="remoteEndPoint"></param>
        /// <param name="sipRequest"></param>
        /// <returns></returns>
        private async Task ByeHandler(SIPEndPoint localSipEndPoint, SIPEndPoint remoteEndPoint, SIPRequest sipRequest)
        {
            Console.WriteLine($"开始处理 [{remoteEndPoint}] 的告别请求...");

            SIPResponse okResponse = SIPResponse.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null);
            await _sipTransport.SendResponseAsync(okResponse);
        }

        /// <summary>
        /// 注册消息（REGISTER）处理方法
        /// </summary>
        /// <param name="localSIPEndPoint"></param>
        /// <param name="remoteEndPoint"></param>
        /// <param name="sipRequest"></param>
        /// <returns></returns>
        private async Task RegisterHandler(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPRequest sipRequest)
        {
            Console.WriteLine($"开始处理 [{remoteEndPoint}] 的注册请求...");
            string sipUserName = sipRequest.Header.From.FromURI.User;

            #region 发送 Trying 消息

            SIPResponse tryingResponse = SIPResponse.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Trying, null);
            await _sipTransport.SendResponseAsync(tryingResponse);

            #endregion

            SIPResponseStatusCodesEnum registerResponse = SIPResponseStatusCodesEnum.Ok;
            bool canLive = false;

            if (sipRequest.Header.Contact?.Count > 0)
            {
                int expiry = sipRequest.Header.Contact[0].Expires > 0
                    ? sipRequest.Header.Contact[0].Expires
                    : sipRequest.Header.Expires;

                //注销;
                if (expiry <= 0)
                {
                    Console.WriteLine($"联系人 [{sipUserName}] 注销.");
                    RemoveContact(sipUserName);

                    return;
                }

                var sipAccount = new SIPAccount(null, sipRequest.Header.From.FromURI.Host, sipUserName, null, null);
                SIPAccountBinding binding = new SIPAccountBinding(sipRequest, sipAccount, sipRequest.Header.Contact[0].ContactURI, remoteEndPoint, localSIPEndPoint, expiry);

                AddContact(sipUserName, binding);
                Console.WriteLine($"注册联系人 [{binding.RegisteredContact}] 为 [{sipAccount.SIPUsername}].");

                canLive = true;
                if (IsAutoLive)
                {
                    SelectedAccountBinding = binding;
                }
            }
            else
            {
                registerResponse = SIPResponseStatusCodesEnum.BadRequest;
            }

            SIPNonInviteTransaction registerTransaction = new SIPNonInviteTransaction(_sipTransport, sipRequest, null);
            SIPResponse okResponse = SIPResponse.GetResponse(sipRequest, registerResponse, null);
            registerTransaction.SendResponse(okResponse);

            //自动发起直播;
            if (canLive && IsAutoLive && !SelectedAccountBinding.IsLived)
            {
                await Task.Delay(1000);
                StartVideoCommand.Execute(null);
            }
        }

        /// <summary>
        /// 添加联系人
        /// </summary>
        /// <param name="sipUserName"></param>
        /// <param name="binding"></param>
        private void AddContact(string sipUserName, SIPAccountBinding binding)
        {
            lock (_lockObj)
            {
                Application.Current.Dispatcher.Invoke(delegate
                {
                    var originBinding = SipRegistrations.FirstOrDefault(x => x.SIPAccount.SIPUsername == sipUserName);
                    if (originBinding != null)
                    {
                        binding.IsLived = originBinding.IsLived;
                        SipRegistrations.Remove(originBinding);
                    }

                    SipRegistrations.Add(binding);
                });
            }
        }

        /// <summary>
        /// 移除联系人
        /// </summary>
        /// <param name="sipUserName"></param>
        private void RemoveContact(string sipUserName)
        {
            lock (_lockObj)
            {
                Application.Current.Dispatcher.Invoke(delegate
                {
                    SipRegistrations.Remove(SipRegistrations.FirstOrDefault(x => x.SIPAccount.SIPUsername == sipUserName));
                });
            }
        }

        /// <summary>
        /// 信息消息（Message）处理方法
        /// </summary>
        /// <param name="localSipEndPoint"></param>
        /// <param name="remoteEndPoint"></param>
        /// <param name="sipRequest"></param>
        /// <returns></returns>
        private async Task MessageHandler(SIPEndPoint localSipEndPoint, SIPEndPoint remoteEndPoint, SIPRequest sipRequest)
        {
            string bodyStr = sipRequest.Body;
            XElement bodyXml = XElement.Parse(bodyStr);
            Dictionary<string, string> infoTypeDict = new Dictionary<string, string>() { ["Catalog"] = "目录", ["DeviceInfo"] = "设备", ["DeviceStatus"] = "状态", };

            string cmdType = bodyXml.Element("CmdType")?.Value;
            switch (cmdType)
            {
                case "Keepalive":
                {
                    Console.WriteLine($"开始处理 [{remoteEndPoint}] 的消息请求（心跳）...");
                    Status = $"{remoteEndPoint} 心跳时间 {DateTime.Now:HH:mm:ss.ffff}";

                    if (SipRegistrations.Any(x => x.SIPAccount.SIPUsername == sipRequest.Header.From.FromURI.User) || IsResponseToNotReg)
                    {
                        await SendOk(sipRequest);
                    }
                    else
                    {
                        Console.WriteLine($"心跳发起者[{sipRequest.Header.From.FromURI}]未注册，不予回应");
                    }

                    break;
                }

                case "Catalog":
                case "DeviceInfo":
                case "DeviceStatus":
                {
                    Console.WriteLine($"[{remoteEndPoint}] 发来【{infoTypeDict[cmdType]}】信息：\r\n{bodyStr}");
                    await SendOk(sipRequest);

                    break;
                }

                default:
                {
                    Console.WriteLine($"Message 类型[{cmdType}]未知，忽略。");
                    break;
                }
            }
        }

        #endregion

        #region 处理 SIP 响应

        /// <summary>
        /// （处理 SIP 响应）Handler for processing incoming SIP responses.
        /// </summary>
        /// <param name="localSIPEndPoint">（[本地]接收者终节点）The end point the response was received on.</param>
        /// <param name="remoteEndPoint">（[远程]发送者终节点）The end point the response came from.</param>
        /// <param name="sipResponse">（接收的 SIP 响应）The SIP response received.</param>
        private async Task SIPTransportResponseReceived(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPResponse sipResponse)
        {
            var method = sipResponse.Header.CSeqMethod;
            var status = sipResponse.Status;
            Console.WriteLine($"从 [{remoteEndPoint}] 收到响应,方法【{method}】,状态[{status}].");

            try
            {
                switch (method)
                {
                    case SIPMethodsEnum.INVITE:
                    {
                        if (status == SIPResponseStatusCodesEnum.Ok)
                        {
                            await InviteOkHandler(sipResponse);
                        }
                        break;
                    }
                    case SIPMethodsEnum.BYE:
                    {
                        if (status == SIPResponseStatusCodesEnum.Ok)
                        {
                            Console.WriteLine("流媒体已关闭");
                        }
                        break;
                    }
                    default:
                    {
                        Console.WriteLine($"收到SIP响应[{method}], 但没有设置处理方法, 略过.");
                        break;
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
        }

        /// <summary>
        /// INVITE 过程中的 200 OK 响应消息处理方法;
        /// </summary>
        /// <param name="sipResponse"></param>
        /// <returns></returns>
        private async Task InviteOkHandler(SIPResponse sipResponse)
        {
            //发送ACK;

            //SIPRequest req = SIPRequest.GetRequest(SIPMethodsEnum.ACK, sipResponse.Header.To.ToURI);
            //req.Header.Contact = new List<SIPContactHeader>(){new SIPContactHeader(sipResponse.Header.From.FromUserField)};

            var from = sipResponse.Header.From;
            var to = sipResponse.Header.To;
            string callId = sipResponse.Header.CallId;

            var accountBinding = SipRegistrations.FirstOrDefault(x => x.RegisteredContact.User == sipResponse.Header.Contact?[0].ContactURI.User);
            if (accountBinding == null)
            {
                Console.WriteLine("未找到合适的账户绑定,跳过.");
                return;
            }

            //保存相关信息供之后（如 BYE）使用;
            accountBinding.CallId = sipResponse.Header.CallId;
            accountBinding.FromTag = sipResponse.Header.From.FromTag;
            accountBinding.ToTag = sipResponse.Header.To.ToTag;

            SIPRequest req = SIPRequest.GetRequest(SIPMethodsEnum.ACK, sipResponse.Header.To.ToURI,
                new SIPToHeader(to.ToName, to.ToURI, to.ToTag),
                new SIPFromHeader(@from.FromName, @from.FromURI, @from.FromTag));

            req.Header.Contact = new List<SIPContactHeader>() { new SIPContactHeader(sipResponse.Header.From.FromName, sipResponse.Header.From.FromURI) };
            req.Header.UserAgent = ConstString.SIP_USERAGENT_STRING;
            req.Header.Allow = null;
            req.Header.Vias = sipResponse.Header.Vias;
            req.Header.CallId = callId; //CallId 要和 200OK 的 CallId 一致，不然 IPC 不认;

            await _sipTransport.SendRequestAsync(accountBinding.RemoteEndPoint, req);

            accountBinding.IsLived = true;
        }

        #endregion

        #region SIP 其它事件

        private static void SIPRequestInTraceEvent(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPRequest sipRequest)
        {
            Console.WriteLine($"收到请求：{remoteEndPoint}->{localSIPEndPoint}: {sipRequest.StatusLine}");
            //Console.WriteLine(sipRequest.ToString());
        }

        private static void SIPRequestOutTraceEvent(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPRequest sipRequest)
        {
            Console.WriteLine($"发出请求：{localSIPEndPoint}->{remoteEndPoint}: {sipRequest.StatusLine}");
            //Console.WriteLine(sipRequest.ToString());
        }

        private static void SIPResponseInTraceEvent(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPResponse sipResponse)
        {
            Console.WriteLine($"收到响应：{remoteEndPoint}->{localSIPEndPoint}: {sipResponse.ShortDescription}");
            //Console.WriteLine(sipResponse.ToString());
        }

        private static void SIPResponseOutTraceEvent(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPResponse sipResponse)
        {
            Console.WriteLine($"发出响应：{localSIPEndPoint}->{remoteEndPoint}: {sipResponse.ShortDescription}");
            //Console.WriteLine(sipResponse.ToString());
        }

        private static void SIPBadRequestInTraceEvent(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, string message, SIPValidationFieldsEnum sipErrorField, string rawMessage)
        {
            Console.WriteLine($"非正常SIP请求. Field={sipErrorField}, Message={message}, Remote={remoteEndPoint}.");
        }

        private static void SIPBadResponseInTraceEvent(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, string message, SIPValidationFieldsEnum sipErrorField, string rawMessage)
        {
            Console.WriteLine($"非正常SIP响应. Field={sipErrorField}, Message={message}, Remote={remoteEndPoint}.");
        }

        #endregion

        #region SIP 方法

        /// <summary>
        /// 获取直播请求Body
        /// </summary>
        /// <param name="userName"></param>
        /// <param name="mediaIp">流媒体服务器IP</param>
        /// <param name="mediaPort">rtp/rtcp媒体端口</param>
        /// <returns>返回 SDP 格式字符串</returns>
        private string GetBodyLive(string userName, string mediaIp, int mediaPort)
        {
            var sdpConn = new GB28181.Net.SDPConnectionInformation(mediaIp);

            var sdp = new GB28181.Net.SDP
            {
                Version = 0,
                SessionId = "0",
                Username = userName,
                SessionName = CommandType.Play.ToString(),
                Connection = sdpConn,
                Timing = "0 0",
                Address = mediaIp
            };

            GB28181.Net.SDPMediaFormat psFormat = new GB28181.Net.SDPMediaFormat(GB28181.Net.SDPMediaFormatsEnum.PS)
            {
                IsStandardAttribute = false
            };
            GB28181.Net.SDPMediaFormat h264Format = new GB28181.Net.SDPMediaFormat(GB28181.Net.SDPMediaFormatsEnum.H264)
            {
                IsStandardAttribute = false
            };

            GB28181.Net.SDPMediaAnnouncement media = new GB28181.Net.SDPMediaAnnouncement
            {
                Media = GB28181.Net.SDPMediaTypesEnum.video, 
                Port = mediaPort,
            };

            media.MediaFormats.Add(psFormat);
            media.MediaFormats.Add(h264Format);
            media.AddExtra("a=recvonly");

            //if (_sipAccount.StreamProtocol == ProtocolType.Tcp)
            //{
            //    media.Transport = "TCP/RTP/AVP";
            //    media.AddExtra("a=setup:" + _sipAccount.TcpMode);
            //    media.AddExtra("a=connection:new");
            //}

            #region 设置ssrc值

            //media.AddExtra("y=0123456789");
            string ssrc = "0" + GetRandomNumber(100000000, 999999999);
            media.AddExtra("y=" + ssrc);

            string ssrcHex = ConvertHelper.ConvertBase(ssrc, 10, 16).ToUpper().PadLeft(8, '0');
            Console.WriteLine($"【SSRC】为：{ssrc}，十六进制为：{ssrcHex}");

            string liveUrl1 = $"http://{Configs.MediaIP}:{Configs.MediaPlayPort}/rtp/{ssrcHex}.flv";
            string liveUrl2 = $"http://{Configs.MediaIP}:{Configs.MediaPlayPort}/rtp/{ssrcHex}/hls.m3u8";
            Console.WriteLine($"媒体服务器 ZLMediaKit 的直播链接为：{liveUrl1} 或 {liveUrl2} 等。");

            #endregion

            media.AddFormatParameterAttribute(psFormat.FormatID, psFormat.Name);
            media.AddFormatParameterAttribute(h264Format.FormatID, h264Format.Name);

            sdp.Media.Add(media);

            return sdp.ToString();
        }

        /// <summary>
        /// 定义随机数产生函数
        /// </summary>
        /// <param name="min"></param>
        /// <param name="max"></param>
        /// <returns></returns>
        public static int GetRandomNumber(int min, int max)
        {
            int rtn = 0;
            Random r = new Random();
            byte[] buffer = Guid.NewGuid().ToByteArray();
            int iSeed = BitConverter.ToInt32(buffer, 0);
            r = new Random(iSeed);
            rtn = r.Next(min, max + 1);
            return rtn;
        }

        /// <summary>
        /// 设置sip主题
        /// </summary>
        /// <returns></returns>
        private string SetSubject()
        {
            return $"{Configs.DeviceId}:{0},{SelectedAccountBinding?.SIPRequest.URI.User}:{new Random().Next(100, ushort.MaxValue)}";
        }

        /// <summary>
        /// 发送 SIP 请求;
        /// </summary>
        /// <returns></returns>
        private async Task SendRequest(SIPMethodsEnum method, string contentType, string subject, string body)
        {
            if (SelectedAccountBinding == null)
            {
                await ConfirmBoxHelper.ShowMessage(DialogVm, "请在左侧选择一个目标项");
                return;
            }

            var to = SelectedAccountBinding.SIPRequest.Header.To;       //Remote
            var from = SelectedAccountBinding.SIPRequest.Header.From;   //(不知为什么和 to 一样的)
            var fromUri = SelectedAccountBinding.SIPRequest.URI;  //Local 使用这个

            SIPRequest req = SIPRequest.GetRequest(method, SelectedAccountBinding.RegisteredContact,
                new SIPToHeader(to.ToName, to.ToURI, ""),
                new SIPFromHeader(fromUri.User, fromUri, "dlgcy"));

            req.Header.Allow = null;
            req.Header.Contact = new List<SIPContactHeader>() { new SIPContactHeader(fromUri.User, fromUri) };
            req.Header.UserAgent = ConstString.SIP_USERAGENT_STRING;
            req.Header.ContentType = contentType;
            req.Header.Subject = subject;
            req.Body = body;

            await _sipTransport.SendRequestAsync(SelectedAccountBinding.RemoteEndPoint, req);
        }

        /// <summary>
        /// 发送 BYE 消息;
        /// </summary>
        /// <param name="accountBinding"></param>
        /// <returns></returns>
        private async Task SendBye(SIPAccountBinding accountBinding)
        {
            SIPRequest req = SIPRequest.GetRequest(SIPMethodsEnum.BYE, accountBinding.RegisteredContact);
            req.Header.CallId = accountBinding.CallId;
            req.Header.From.FromTag = accountBinding.FromTag;
            req.Header.To.ToTag = accountBinding.ToTag;

            await _sipTransport.SendRequestAsync(accountBinding.RemoteEndPoint, req);
            accountBinding.IsLived = false;
        }

        /// <summary>
        /// 发送OK消息;
        /// </summary>
        /// <param name="sipRequest"></param>
        /// <returns></returns>
        private async Task SendOk(SIPRequest sipRequest)
        {
            SIPResponseStatusCodesEnum messageResponse = SIPResponseStatusCodesEnum.Ok;
            SIPResponse okResponse = SIPResponse.GetResponse(sipRequest, messageResponse, null);
            //SIPNonInviteTransaction messageTransaction = new SIPNonInviteTransaction(_sipTransport, sipRequest, null);
            //messageTransaction.SendResponse(okResponse);
            await _sipTransport.SendResponseAsync(okResponse);
        }

        /// <summary>
        /// 获取查询设备信息Body
        /// </summary>
        /// <returns>返回 MANSCDP 格式字符串</returns>
        private string GetBodyQueryInfo(CommandType commandType)
        {
            var body = new CatalogQuery()
            {
                CommandType = commandType,
                DeviceID = SelectedAccountBinding.RegisteredContact.User,
                SN = new Random().Next(1, ushort.MaxValue)
            };

            string xmlBody = CatalogQuery.Instance.Save<CatalogQuery>(body);

            return xmlBody;
        }

        #endregion
    }
}
